package lt.liepasoft.locr.gui;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JEditorPane;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import lt.liepasoft.locr.ImageManager;
import lt.liepasoft.locr.Recognizer;
import lt.liepasoft.locr.Scanner;
import lt.liepasoft.locr.data.OcrImage;
import lt.liepasoft.locr.data.RgbImage;
import lt.liepasoft.locr.data.Symbol;
import lt.liepasoft.locr.data.SymbolImage;
import lt.liepasoft.locr.realization.EncryptedSymbols;
import lt.liepasoft.locr.realization.FloodFillScanner;
import lt.liepasoft.locr.realization.SymbolRecognizer;
import lt.liepasoft.locr.utils.ImageUtils;

public class OcrMainFrame extends JFrame {
    /**
	 * 
	 */
	private static final long serialVersionUID = 1343533771934661505L;
	private ImageUtils utils;
    
    //0-original 1-grayscale 2-black&white    
    private RgbImage rgbImage;
    private OcrImage ocrImage;
    private BufferedImage[] image;
    private JEditorPane editorPane;
    private OcrImageRenderPanel imagePanel;
    
    private int imgW = 1000, imgH = 1000;
    
    private static OcrMainFrame instance;
    
    private JPanel mainPanel;
    
    private JCheckBox 
                normalizeCheckBox,
                invertCheckBox;
    
    private JSlider
                treasureHoldSpinner;
    
    private OcrSymbolsList 
                symbolsList;
    
    
    private static Scanner mScanner;    
    private static Map<Character, SymbolImage> mSymbolsLibrary;
    private static Recognizer mRecognizer;
    
    
    
    private List <Symbol> symbols;
    
    private ButtonGroup 
                radioGroup;
    
    private JRadioButton[] 
                radioButtons;
    
    private SymbolCompareDialog 
                mSymbolCompareDialog;
    
    public static OcrMainFrame getInstance(){
    	if (instance == null){
    		instance = new OcrMainFrame();
    	}
    	return instance;
    }
    
    private OcrMainFrame() {
        super("LIEPA OCR (beta)");
        setLookandFeel();
        
        utils= ImageUtils.getInstance();
        
        image = new BufferedImage[3];
        
        mScanner = new FloodFillScanner();
        
        mSymbolsLibrary = new EncryptedSymbols().symbols;        
        mRecognizer = new SymbolRecognizer(mSymbolsLibrary);               
        
        mainPanel = new JPanel(new BorderLayout());
        mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 0, 10));
                
        //top panel
        JPanel topPanel  = new JPanel(new FlowLayout(FlowLayout.LEFT));
//        JPanel topPanelPanel = new JPanel();
//        topPanelPanel.add();                
        topPanel.add(createFilterMenuPanel());

        //left panel
        JPanel leftPanel = new JPanel(new BorderLayout());
        leftPanel.setBorder(BorderFactory.createEtchedBorder());
        leftPanel.add(createSymbolsListPanel(), BorderLayout.CENTER);
        //center panel
        JPanel centerPanel = new JPanel(new BorderLayout());
        centerPanel.setBorder(BorderFactory.createEtchedBorder());
        centerPanel.add(createRadioButtonsPanel(),BorderLayout.NORTH);
        centerPanel.add(createImagePanel(), BorderLayout.CENTER);
        centerPanel.add(createRecognizedTextPanel(), BorderLayout.SOUTH);
        
        
        //bottom panel
        JPanel bottomPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
        bottomPanel.add(new JLabel("Copyright (c) 2012 Tautvydas Liepa"));
        
        mainPanel.add(topPanel, BorderLayout.NORTH);
        mainPanel.add(centerPanel, BorderLayout.CENTER);
        mainPanel.add(leftPanel, BorderLayout.EAST);        
        mainPanel.add(bottomPanel, BorderLayout.SOUTH);    
        
        setJMenuBar(createMenuBar());
        
        setSize(700, 700);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setContentPane(mainPanel);
        setVisible(true);    
        mainPanel.updateUI();  
        
        
    }
    
    private JPanel createRadioButtonsPanel(){
        JPanel p = new JPanel();
        
        // Register an action listener for the radio buttons.
        ActionListener myListener = new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                showSelectedImage();
            }
        };
        
        // Group the radio buttons.
        radioGroup = new ButtonGroup();
        radioButtons = new JRadioButton[3];
        // Create the radio buttons and assign Keyboard shortcuts using Mnemonics
        JRadioButton radioButton;
        
        radioButton = new JRadioButton("Original");
        radioButton.setActionCommand("Original");
        radioButton.setSelected(true);
        radioButton.addActionListener(myListener);
        radioButtons[0]=radioButton;
        radioGroup.add(radioButton);
        p.add(radioButton);

        radioButton = new JRadioButton("Grayscale");
        radioButton.setActionCommand("Grayscale");
        radioButton.setSelected(true);
        radioButton.addActionListener(myListener);
        radioButtons[1]=radioButton;
        radioGroup.add(radioButton);
        p.add(radioButton);
        
        radioButton = new JRadioButton("Black&White");
        radioButton.setActionCommand("Black&White");
        radioButton.setSelected(true);
        radioButton.addActionListener(myListener);               
        radioButtons[2]=radioButton;
        radioGroup.add(radioButton);        
        p.add(radioButton);

        return p;
    }
    
    private JPanel createFilterMenuPanel(){
        JPanel p = new JPanel();        
        JPanel pp = new JPanel(new GridLayout(2, 1));
        normalizeCheckBox = new JCheckBox("Normalize");
        normalizeCheckBox.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {    
                if (rgbImage != null){
                    ocrImage = ImageManager.createOcrImage(rgbImage, normalizeCheckBox.isSelected());
                    if (radioGroup.getSelection().equals(radioButtons[0].getModel()))
                        radioGroup.setSelected(radioButtons[1].getModel(), true);
                    showSelectedImage();
                }   
            }
        });        
        normalizeCheckBox.setSelected(true);
        pp.add(normalizeCheckBox);        
        
        invertCheckBox = new JCheckBox("Invert");
        invertCheckBox.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (radioGroup.getSelection().equals(radioButtons[0].getModel()))
                    radioGroup.setSelected(radioButtons[1].getModel(), true);
                showSelectedImage();
            }
        });           
        pp.add(invertCheckBox);
                
        p.add(pp);
        p.add(createSliderPanel());     
        p.add(createButtonsPanel());

        return p;
    }
    
    private JPanel createSliderPanel(){
        JPanel p = new JPanel();
        treasureHoldSpinner = new JSlider(JSlider.HORIZONTAL,
                                      -128, 128, 0);
        
        treasureHoldSpinner.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                radioGroup.setSelected(radioButtons[2].getModel(), true);
                showSelectedImage();
            }
        });

        //Turn on labels at major tick marks.
        //treasureHoldSpinner.setMajorTickSpacing(50);
        treasureHoldSpinner.setMinorTickSpacing(1);
        //treasureHoldSpinner.setPaintTicks(true);
        //treasureHoldSpinner.setPaintLabels(true);        
        p.add(treasureHoldSpinner);
        
        return p;
    }
    
    private JPanel createButtonsPanel(){
        JPanel p = new JPanel(new FlowLayout(FlowLayout.CENTER, 10,10));
        JButton b;
        
        b = new JButton("Scan image");
        b.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (ocrImage != null){
                    mScanner.setWhiteThreshold(treasureHoldSpinner.getValue());
                    mScanner.setInvert(invertCheckBox.isSelected());
                    symbols = mScanner.scan(ocrImage, ocrImage.bounds);
                    System.out.println("found symbols count: "+symbols.size());
                    
                    String text="";
                    List <Symbol> recognizedSymbols = new ArrayList<>();
                    for (Symbol s: symbols){
                        Character c = mRecognizer.recognize(s.getImage());
                        s.setCharacter(c);
                        recognizedSymbols.add(s);
                        text += c;
                        
                    }
                    
                    symbolsList.setSymbolsList(recognizedSymbols);
                    getSymbolCompareDialog().symbolSelectionPanel.setSymbols(listTranslator(symbols));
                    //symbolComparePanel.updateUI();
                    

                    editorPane.setText(text);                        
                    
                    mainPanel.updateUI();
                }    
            }
        });
        p.add(b);
              
        b = new JButton("Show symbols compare"){
            @Override
            public boolean isEnabled() {
                return(symbols != null && !symbols.isEmpty());                                
            }                                    
        };
        b.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                showSymbolsCompareDialog();
            }                        
        });
        p.add(b);
        
        return p;
    }        
    
    private JPanel createLogoPanel(){
        JPanel p = new JPanel();
        p.add(new JLabel("logo panel"));
        return p;
    }
    
    private JPanel createSymbolsListPanel(){
        symbolsList = new OcrSymbolsList();
        symbolsList.addListSelectionListener(new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {                
                if(!e.getValueIsAdjusting()){                    
                	Symbol s = symbolsList.getSelectedSymbol();
                        imagePanel.setSelectedSymbol(s);
                }
            }
        });
        return symbolsList;
    }     
    
    private JScrollPane createImagePanel(){
        imagePanel = new OcrImageRenderPanel();
        imagePanel.setOpaque(true);
        JScrollPane pane = new JScrollPane(imagePanel);
        pane.setOpaque(true);        
        return pane;
    }
    
    private JScrollPane createRecognizedTextPanel(){
        editorPane = new JEditorPane();
        editorPane.setEditable(false);

        JScrollPane editorScrollPane = new JScrollPane(editorPane);
        editorScrollPane.setVerticalScrollBarPolicy(
                JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        editorScrollPane.setHorizontalScrollBarPolicy(
                JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        editorScrollPane.setPreferredSize(new Dimension(250, 75));
        return editorScrollPane;
    }
    
    private JMenuBar createMenuBar(){
        JMenuBar menuBar = new JMenuBar();
        JMenu menu;
        JMenuItem item;
        
        menu = new JMenu("Image");        
        item = new JMenuItem("Load image");
        item.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JFileChooser chooser = new JFileChooser();
                chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);

                int returnVal = chooser.showOpenDialog(null);
                if (returnVal == JFileChooser.APPROVE_OPTION)
                {
                    String imgDir = (chooser.getSelectedFile().getAbsolutePath());
                    setFrameTitle(imgDir);
                    System.out.println(imgDir);
                    try {
                        File outputfile;
                        outputfile = new File("grayscale.png");
                        
                        image[0] = utils.readImage(imgDir);  
                        image[0] = utils.scaleImage(image[0], imgW, imgH, true);
//                        ImageIO.write(image[0], "png", outputfile);
                        rgbImage  = utils.getImage(image[0]);
                        //rgbImage = manager.scale(rgbImage, 1000, 1000, true);
                        image[0] = utils.rgbImageToImage(rgbImage);
                        ocrImage = ImageManager.createOcrImage(rgbImage, normalizeCheckBox.isSelected());
                        showSelectedImage();
                    } catch (Exception ex) {
                        ex.printStackTrace();
                        JOptionPane.showMessageDialog(imagePanel, ex.getMessage());
                        image[0]=null;                         
                        image[1]=null;
                        image[2]=null;
                    } finally {
                        imagePanel.setSelectedSymbol(null);
                        symbolsList.clear();                        
                    }
                }
            }
        });
        menu.add(item);        
        
        item = new JMenuItem("Load URL image");
        item.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
            	try {
            		String value = UrlInputDialog.showDialog();
            		if (value == null) return;
	        		URL url = new URL(value);
	        		image[0] = ImageIO.read(url); 
                    image[0] = utils.scaleImage(image[0], imgW, imgH, true);
//                    ImageIO.write(image[0], "png", outputfile);
                    rgbImage  = utils.getImage(image[0]);
                    //rgbImage = manager.scale(rgbImage, 1000, 1000, true);
                    image[0] = utils.rgbImageToImage(rgbImage);
                    ocrImage = ImageManager.createOcrImage(rgbImage, normalizeCheckBox.isSelected());
                    showSelectedImage();
            	} catch (Exception ex){
            		ex.printStackTrace();
                    JOptionPane.showMessageDialog(imagePanel, ex.getMessage());
                    image[0]=null;                         
                    image[1]=null;
                    image[2]=null;
            	} finally {
                    imagePanel.setSelectedSymbol(null);
                    symbolsList.clear();                        
                }
        		
            }
          
        });
        menu.add(item);        
        
        menuBar.add(menu);
        
        menu = new JMenu("Help");
        item = new JMenuItem("About");
        menu.add(item);        
        item = new JMenuItem("Help");
        menu.add(item);        
        
        menuBar.add(menu);        
        return menuBar;
    }
    
    private void showSelectedImage(){     
        
        String action = radioGroup.getSelection().getActionCommand();
        if ("Original".equals(action)){
            imagePanel.setMainImage(image[0]);
        } else
        if ("Grayscale".equals(action)){
            if (ocrImage != null)
                image[1] = utils.rgbImageToImage(ImageManager.createRgbImage(ocrImage, -129, invertCheckBox.isSelected()));
            imagePanel.setMainImage(image[1]);
        } else
        if ("Black&White".equals(action)){
            if (ocrImage != null)
                image[2] = utils.rgbImageToImage(ImageManager.createRgbImage(ocrImage, treasureHoldSpinner.getValue(), invertCheckBox.isSelected()));
            imagePanel.setMainImage(image[2]);
        }                    
        mainPanel.updateUI();
    }

    public void setFrameTitle(String title){
        setTitle("LIEPA OCR (beta) "+title);
    }
    
    private void setLookandFeel() {    
            try {
                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            } catch (Exception e){}
    }
    
    private SymbolCompareDialog getSymbolCompareDialog(){
    	if (mSymbolCompareDialog == null)
    		mSymbolCompareDialog = new SymbolCompareDialog();
    	return mSymbolCompareDialog;
    }
    
    private void showSymbolsCompareDialog(){
    	getSymbolCompareDialog().setVisible(true);
    }
    
    public static Scanner getScanner(){
    	return getInstance().mScanner;
    }
    
    public static Recognizer getRecognizer(){
    	return getInstance().mRecognizer;
    }
    
    public static Map<Character, SymbolImage> getKnownSymbolsMap(){
    	return getInstance().mSymbolsLibrary;
    }
    
    public static List <SymbolImage> listTranslator(List <Symbol> symbols){
    	List <SymbolImage> images = new ArrayList<>();
    	for (Symbol s: symbols){
    		images.add(s.getImage());
    	}
    	return images;
    }
    
}
